-module(robust_bank_server).

-export([bstart/1, start/1, stop/1]).

%% make two windows
%% 1) In window one
%%      $ erl -sname one -mnesia dir '"one"'
%%      robust_bank_server:start(3020).
%% 2) In window two
%%      $ erl -sname two -mnesia dir '"two"'
%%      robust_bank_server:start(3030).

-include("reply.hrl").

bstart([APort]) ->
    Port = list_to_integer(atom_to_list(APort)),
    start(Port).

start(Port) ->
    mnesia:start(),
    spawn_link(fun() -> server(Port) end).

stop(Port) ->
    mnesia:stop(),
    tcp_server:stop(Port).

server(Port) ->
    tcp_server:start_raw_server(Port, fun(Socket) ->
                                            input_handler(Socket, Port)
                                      end, 15, 4).

input_handler(Socket, Port) ->
    receive
        {tcp, Socket, Bin} ->
            case binary_to_term(Bin) of
                {call, Tag, Term} ->
                    io:format("Server port: ~p Tag: ~p call: ~p~n",
                           [Port, Tag, Term]),
                    Reply = do_call(Tag, Term),
                    send_term(Socket, {ack, Tag, Reply}),
                    input_handler(Socket, Port);
                {delete_tag, Tag} ->
                    io:format("Server port: ~p deleteTag: ~p~n", 
                           [Port, Tag]),
                    delete_tag(Tag)
            end;
        {tcp_closed, Socket} ->
            true
    end.

send_term(Socket, Term) ->
    gen_tcp:send(Socket, [term_to_binary(Term)]).

do_call(Tag, C) ->
    Fun = the_func(C),
    F = fun() ->
            case mnesia:read({reply, Tag}) of
                [] ->
                    % no cached value
                    Val = Fun(),
                    mnesia:write(#reply{tag=Tag, val=Val}),
                    Val;
                [C] ->
                    % yes - return
                    C#reply.val
            end
        end,
    mnesia:transaction(F).

the_func({deposit, Who, X})     -> bank:deposit(Who, X);
the_func({withdraw, Who, X})    -> bank:withdraw(Who, X);
the_func({balance, Who})        -> bank:balance(Who).

delete_tag(Tag) ->
    F = fun() -> mnesia:delete({reply, Tag}) end,
    V = mnesia:transaction(F),
    io:format("delete tag= ~p~n", [V]).
